/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Base Sound class.
 *//*
 * LEGAL:   COPYRIGHT (C) 2009 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define SOUND_SOUND_BASE_CC 1
#include <utility>
#include "base/module.hh"
#include "base/conf.hh"
#include "base/file.hh"
#include "base/stream.hh"
using namespace base;
#include "sound/module.hh"
#include "sound/sound_base.hh"

namespace sound {

#define RETURN_IF_SOUND_DISABLED                            \
{{                                                          \
    if ( UX( not IfEnabled() ) )                            \
        return true;  /* return false only if error */      \
}}

////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////  SoundBase  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
SoundBase::SoundBase( void )
:   mSampleMap()
{
    // NOP
}

SoundBase::~SoundBase()
{
    // NOP
}

/*****************************************************************************
 * @return True if sample is playing.
 *****************************************************************************/
bool
SoundBase::IfPlaying( const Sample::Name& sampleName )
{
    RETURN_IF_SOUND_DISABLED;

    // Samples are cached in a STL map.
    // They may be still playing or finished playing.
    SampleMap::const_iterator iter = mSampleMap.find( sampleName );
    if ( iter != mSampleMap.end() )
    {
        // Sample is in cache.  Still playing?
        CHECK_TYPESIG(iter->second,TYPESIG_SOUND_SAMPLE);
        return iter->second->IfPlaying();  // second=shptr<Sample>
    }
    else
    {
        // Sample isn't in cache.
        return false;  // isn't an error
    }
}

/*****************************************************************************
 * Play sound sample.
 * @param   sampleName
 * @param   playLoop
 *          Play continuously or once.
 * @param   volume
 *          Default is DEFAULT_VOLUME.
 * @return false if error (else true, even if sound is disabled).
 *****************************************************************************/
bool
SoundBase::Play( const Sample::Name& sampleName,
                 const defs::ePlayLoop playLoop,
                 const fp volume )
{
    RETURN_IF_SOUND_DISABLED;

    // Is it playing?
    if ( IfPlaying( sampleName ) )
    {
        // Yes.  May need to change its volume (?).
        return SetVolume( sampleName, volume );
    }

    // Not playing.
    try
    {
        // Is sample cached?
        SampleMap::iterator iter = mSampleMap.find( sampleName );
        if ( iter != mSampleMap.end() )
        {
            // Was cached, play it again.
            CHECK_TYPESIG(iter->second,TYPESIG_SOUND_SAMPLE);
            iter->second->Play( playLoop, volume );  // second=shptr<Sample>
        }
        else
        {
            // Load and cache sample.
            shptr<Sample> sample = CreateSample( sampleName );  // creates derived sample --MIGHT THROW EXCEPTION--
            mSampleMap.insert( std::make_pair( sampleName, sample ) );

            // Play.
            sample->Play( playLoop, volume );
        }

        return true;
    }
    catch ( const std::exception& ex )  // Sample ctor might throw exception
    {
        //#if LOG_SOUND_ERRORS // tell user that sound data files are missing
        CERROR << "ERROR[SoundBase::Play] cannot play sound file " << sampleName << std::endl
               << ex.what() << std::endl;
        //#endif
        return false;
    }
}

/*****************************************************************************
 * Stop playing all samples.
 *****************************************************************************/
bool
SoundBase::Stop( void )
{
    RETURN_IF_SOUND_DISABLED;

    // For each sample.
    bool ok = true;
    for ( SampleMap::iterator iter = mSampleMap.begin();
          iter != mSampleMap.end();
          ++iter )
    {
        CHECK_TYPESIG(iter->second,TYPESIG_SOUND_SAMPLE);
        if ( UX( not iter->second->Stop() ) )  // Sample::Stop()
        {
            #if LOG_SOUND_ERRORS
            CERROR << "ERROR[SoundBase::Stop] " << iter->second->GetName() << std::endl;
            #endif
            ok = false;  // continue
        }
    }
    return ok;
}

/*****************************************************************************
 * Stop playing a sample.
 *****************************************************************************/
bool
SoundBase::Stop( const Sample::Name& sampleName )
{
    RETURN_IF_SOUND_DISABLED;

    // Find sample.
    SampleMap::iterator iter = mSampleMap.find( sampleName );
    if ( iter != mSampleMap.end() )
    {
        CHECK_TYPESIG(iter->second,TYPESIG_SOUND_SAMPLE);
        return iter->second->Stop();  // Sample::Stop()
    }
    else
    {
        //#if LOG_SOUND_ERRORS
        //CERROR << "ERROR[SoundBase::Stop] " << sampleName << " was never played." << std::endl;
        //#endif
        return false;
    }
}

/*****************************************************************************
 * @return Volume of a sample.
 *****************************************************************************/
fp
SoundBase::GetVolume( const Sample::Name& sampleName )
{
    // Disabled?
    if ( not IfEnabled() )
        return 0.0f;

    // Is sample cached?
    SampleMap::const_iterator iter = mSampleMap.find( sampleName );
    if ( iter != mSampleMap.end() )
    {
        CHECK_TYPESIG(iter->second,TYPESIG_SOUND_SAMPLE);
        return iter->second->GetVolume();
    }
    else
    {
        // Was never played.
        return 0.0f;
    }
}

/*****************************************************************************
 * Set volume of a sample.
 * @param   sampleName
 * @param   volume
 *          0.0 = off, 1.0 max volume.
 *****************************************************************************/
bool
SoundBase::SetVolume( const Sample::Name& sampleName, const fp volume )
{
    RETURN_IF_SOUND_DISABLED;

    // No change to volume?
    if ( volume == GetVolume(sampleName) )
        return true;

    // Need to change volume.
    SampleMap::iterator iter = mSampleMap.find( sampleName );
    if ( iter != mSampleMap.end() )
    {
        CHECK_TYPESIG(iter->second,TYPESIG_SOUND_SAMPLE);
        iter->second->SetVolume( volume );  // Sample::SetVolume()
        return true;
    }
    else
    {
        #if LOG_SOUND_ERRORS
        CERROR << "ERROR[SoundBase::SetVolume] " << sampleName << std::endl;
        #endif
        return false;
    }
}

/*****************************************************************************
 * Compute full pathname to a sound file according to these rules:
 * - If arg is a pure filename (no slashes) then prepends directory to sound data files.
 * - If arg has any slashes then it is returned unchanged (relative or absolute pathname).
 *****************************************************************************/
const string  // CLASS_METHOD
SoundBase::ComputePathnameSoundFile( const string& soundFilename )
{
    if ( IfPathIsFilename( soundFilename ) )
        return string(GetDataDir()) + string("/sounds/") + soundFilename;
    else
        return soundFilename;
}

} // namespace sound
